package state.bluetooth;

import java.io.IOException;

import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;

import state.Control;

/**
 * The ServerThread is used to wait until someone connects. <br>
 */
public class BluetoothServer extends Control {
	private final int MAX_CONN = 4;

	private LocalDevice localDevice;
	private StreamConnectionNotifier notifier;
	private BluetoothConnection[] connections = new BluetoothConnection[MAX_CONN];

	private String serviceUUID;
	private String serverName;
	private int previousDiscoverabilityMode = -1;
	private boolean isRunning;
	private ServerListener serverListener;
	private ProtocolListener listener;

	protected BluetoothServer(String serviceUUID, String serverName, LocalDevice localDevice, ServerListener serverListener, ProtocolListener listener) {
		this.serviceUUID = serviceUUID;
		this.serverName = serverName;
		this.localDevice = localDevice;
		this.serverListener = serverListener;
		this.listener = listener;
		start();
	}
	
	public void setServerListener(ServerListener serverListener) {
		for (int i = 0; i < connections.length; i++) {
			if (connections[i] != null) {
				connections[i].setServerListener(serverListener);
			}
		}
		this.serverListener = serverListener;
	}

	public void setProtocolListener(ProtocolListener listener) {
		for (int i = 0; i < connections.length; i++) {
			if (connections[i] != null) {
				connections[i].setProtocolListener(listener);
			}
		}
		this.listener = listener;
	}

	public void perform() {
		isRunning = true;
		new ConnectivityChecker().start();
		
		try {
			localDevice.setDiscoverable(DiscoveryAgent.GIAC);
			notifier = (StreamConnectionNotifier) Connector.open("btspp://localhost:" + serviceUUID + ";name=server" + ";authorize=false;authenticate=false;encrypt=false");
		} catch (Exception e) {
			return;
		}

		saveDiscoverability();
		while (isRunning) {
			// Register service
			try {
				// Wait on client
				StreamConnection conn = (StreamConnection) notifier.acceptAndOpen();
				int freeIndex = getFreeSpaceOfConnArray();

				if (freeIndex == -1) { // Nếu đã hết chỗ trống thì đóng conn
					conn.close();
					continue;
				}

				connections[freeIndex] = new BluetoothConnection(conn, serverName, "Host", listener, serverListener);
				connections[freeIndex].setStoredIndex(freeIndex);

				// Read host name
				String remoteName = connections[freeIndex].recieveString();
				connections[freeIndex].setRemoteName(remoteName);
				connections[freeIndex].send(serverName);
				connections[freeIndex].start();
				if (serverListener != null) {
					try {
						serverListener.onNewClientConnect(connections[freeIndex]);
					} catch (Throwable e) {
						e.printStackTrace();
					}
				}
			} catch (Exception e) {
			}
		}

		if (notifier != null) {
			try {
				notifier.close();
			} catch (IOException e) {
			}
			notifier = null;
		}

		// restore discoverability mode
		restoreDiscoverability();
	}

	public void releaseConn(int index) {
		if (connections[index] != null) {
			connections[index].close();
			connections[index] = null;
		}
	}

	private int getFreeSpaceOfConnArray() {
		for (int i = 0; i < connections.length; i++) {
			if (connections[i] == null) {
				return i;
			}
		}
		return -1;
	}

	public BluetoothConnection[] getConnections() {
		return connections;
	}

	private void saveDiscoverability() {
		try {
			previousDiscoverabilityMode = localDevice.getDiscoverable();
		} catch (Exception e) {
		}
	}

	private void restoreDiscoverability() {
		try {
			if (previousDiscoverabilityMode != -1) {
				localDevice.setDiscoverable(previousDiscoverabilityMode);
			}
		} catch (Exception e) {
		}
	}

	protected void detroy() {
		isRunning = false;
		localDevice = null;
		serverListener = null;

		new Thread() {
			public void run() {
				if (notifier != null) {
					try {
						notifier.close();
					} catch (IOException e) {
					}
					notifier = null;
				}

				for (int i = 0; i < connections.length; i++) {
					if (connections[i] != null) {
						connections[i].close();
						connections[i] = null;
					}
				}
			}
		}.start();
	}
	
	private class ConnectivityChecker extends Control {
		public void perform() {
			while (isRunning) {
				for (int i = 0; i < connections.length; i++) {
					if (connections[i] == null) {
						continue;
					}
					
					if (connections[i].isClosed()) {
						connections[i] = null;
						continue;
					}
					long now = System.currentTimeMillis();
					
					// Kiểm tra nếu quá 3s mà có client nào ko gửi data thì đóng connection
					if (now - connections[i].getLastTimeRecive() > BluetoothUtil.DEFAULT_TIME_OUT_TO_DISCONNECT) {
						connections[i].close();
						listener.onDisconnect(connections[i]);
						connections[i] = null;
						continue;
					}
					
					if (now - connections[i].getLastTimeSend() > BluetoothUtil.HEART_BREATH_SEQUENCE_TIME) {
						connections[i].sendCommand(BluetoothConnection.HEART_BREATH_SIGNAL); // Gửi HEART_BREATH_SIGNAL
					}
				}
				
				try {
					sleep(BluetoothUtil.HEART_BREATH_SEQUENCE_TIME);
				} catch (InterruptedException e) {
				}
			}
		}
	}
}